#pragma warning( disable : 4996 )

#include <Windows.h>
#include <time.h>
#include <map>

#include "MemTrack.h"

#ifdef _DEBUG

class CritSect 
{
private:
	CRITICAL_SECTION mCriticalSection;
	friend class AutoCrit;

public:
	CritSect()
	{
		InitializeCriticalSection(&mCriticalSection);
	}

	~CritSect()
	{
		DeleteCriticalSection(&mCriticalSection);
	}
};


class AutoCrit
{
	LPCRITICAL_SECTION		mCritSec;

public:
	AutoCrit(LPCRITICAL_SECTION theCritSec) : 
	  mCritSec(theCritSec)
	  { 
		  EnterCriticalSection(mCritSec); 
	  }

	  AutoCrit(const CritSect& theCritSect) : 
	  mCritSec((LPCRITICAL_SECTION) &theCritSect.mCriticalSection)
	  { 
		  EnterCriticalSection(mCritSec); 
	  }

	  ~AutoCrit()
	  { 
		  LeaveCriticalSection(mCritSec); 
	  }
};



///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
struct SEXY_ALLOC_INFO
{
	int		size;
	char	file[64];
	int		line;
};

static bool gShowLeaks = false;
static bool gSexyAllocMapValid = false;
class SexyAllocMap : public std::map<void*, SEXY_ALLOC_INFO>
{
public:
	CritSect mCrit;

public:
	SexyAllocMap() { gSexyAllocMapValid = true; }
	~SexyAllocMap() 
	{ 
		if (gShowLeaks) 
			SexyDumpUnfreed();		

		gSexyAllocMapValid = false; 
	}
};
static SexyAllocMap gSexyAllocMap;

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void SexyMemAddTrack(void *addr,  int asize,  const char* fname, int lnum)
{
	if (!gSexyAllocMapValid)
		return;

	AutoCrit aCrit(gSexyAllocMap.mCrit);
	gShowLeaks = true;

	SEXY_ALLOC_INFO &info = gSexyAllocMap[addr];
	strncpy(info.file, fname, 63);
	info.line = lnum;
	info.size = asize;
};

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void SexyMemRemoveTrack(void* addr)
{
	if (!gSexyAllocMapValid)
		return;

	AutoCrit aCrit(gSexyAllocMap.mCrit);
	SexyAllocMap::iterator anItr = gSexyAllocMap.find(addr);
	if (anItr != gSexyAllocMap.end())
		gSexyAllocMap.erase(anItr);
};

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void SexyDumpUnfreed()
{
	if (!gSexyAllocMapValid)
		return;

	AutoCrit aCrit(gSexyAllocMap.mCrit);
	SexyAllocMap::iterator i;
	int totalSize = 0;
	char buf[8192];

	FILE* f = fopen("mem_leaks.txt", "wt");
	if (!f)
		return;

	time_t aTime = time(NULL);
	sprintf(buf, "Memory Leak Report for %s\n",	asctime(localtime(&aTime)));
	fprintf(f, buf);
	OutputDebugString("\n");
	OutputDebugString(buf);
	for(i = gSexyAllocMap.begin(); i != gSexyAllocMap.end(); i++) 
	{
		sprintf(buf, "%s(%d) : Leak %d byte%s\n", i->second.file, i->second.line, i->second.size,i->second.size>1?"s":"");
		OutputDebugString(buf);
		fprintf(f, buf);
		totalSize += i->second.size;
	}


	sprintf(buf, "-----------------------------------------------------------\n");
	fprintf(f, buf);
	OutputDebugString(buf);
	sprintf(buf, "Total Unfreed: %d bytes\n\n", totalSize);
	OutputDebugString(buf);
	fprintf(f, buf);
}

#endif
